/**
 * Project: phoenix-load-balancer
 * <p/>
 * File Created at Dec 11, 2013
 */
package com.dianping.phoenix.lb.utils;

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * Assists in implementing {@link Object#equals(Object)} methods.
 * </p>
 *
 * <p>
 * This class provides methods to build a good equals method for any class. It
 * follows rules laid out in <a
 * href="http://java.sun.com/docs/books/effective/index.html">Effective Java</a>
 * , by Joshua Bloch. In particular the rule for comparing <code>doubles</code>,
 * <code>floats</code>, and arrays can be tricky. Also, making sure that
 * <code>equals()</code> and <code>hashCode()</code> are consistent can be
 * difficult.
 * </p>
 *
 * <p>
 * Two Objects that compare as equals must generate the same hash code, but two
 * Objects with the same hash code do not have to be equal.
 * </p>
 *
 * <p>
 * All relevant fields should be included in the calculation of equals. Derived
 * fields may be ignored. In particular, any field used in generating a hash
 * code must be used in the equals method, and vice versa.
 * </p>
 *
 * <p>
 * Typical use for the code is as follows:
 * </p>
 *
 * <pre>
 * public boolean equals(Object obj) {
 *     if (obj == null) {
 *         return false;
 *     }
 *     if (obj == this) {
 *         return true;
 *     }
 *     if (obj.getClass() != getClass()) {
 *         return false;
 *     }
 *     MyClass rhs = (MyClass) obj;
 *     return new AdvEqualsBuilder().appendSuper(super.equals(obj)).append(field1, rhs.field1).append(field2, rhs.field2)
 *             .append(field3, rhs.field3).isEquals();
 * }
 * </pre>
 *
 * <p>
 * Alternatively, there is a method that uses reflection to determine the fields
 * to test. Because these fields are usually private, the method,
 * <code>reflectionEquals</code>, uses
 * <code>AccessibleObject.setAccessible</code> to change the visibility of the
 * fields. This will fail under a security manager, unless the appropriate
 * permissions are set up correctly. It is also slower than testing explicitly.
 * </p>
 *
 * <p>
 * A typical invocation for this method would look like:
 * </p>
 *
 * <pre>
 * public boolean equals(Object obj) {
 *     return AdvEqualsBuilder.reflectionEquals(this, obj);
 * }
 * </pre>
 *
 * @author <a href="mailto:steve.downey@netfolio.com">Steve Downey</a>
 * @author Stephen Colebourne
 * @author Gary Gregory
 * @author Pete Gieser
 * @author Arun Mammen Thomas
 * @since 1.0
 * @version $Id: AdvEqualsBuilder.java 611543 2008-01-13 07:00:22Z bayard $
 */
public class AdvEqualsBuilder {

	/**
	 * If the fields tested are equals. The default value is <code>true</code>.
	 */
	private boolean isEquals = true;

	/**
	 * <p>
	 * Constructor for AdvEqualsBuilder.
	 * </p>
	 *
	 * <p>
	 * Starts off assuming that equals is <code>true</code>.
	 * </p>
	 *
	 * @see Object#equals(Object)
	 */
	public AdvEqualsBuilder() {
		// do nothing for now.
	}

	// -------------------------------------------------------------------------

	/**
	 * <p>
	 * This method uses reflection to determine if the two <code>Object</code>s
	 * are equal.
	 * </p>
	 *
	 * <p>
	 * It uses <code>AccessibleObject.setAccessible</code> to gain access to
	 * private fields. This means that it will throw a security exception if run
	 * under a security manager, if the permissions are not set up correctly. It
	 * is also not as efficient as testing explicitly.
	 * </p>
	 *
	 * <p>
	 * Transient members will be not be tested, as they are likely derived
	 * fields, and not part of the value of the Object.
	 * </p>
	 *
	 * <p>
	 * Static fields will not be tested. Superclass fields will be included.
	 * </p>
	 *
	 * @param lhs
	 *            <code>this</code> object
	 * @param rhs
	 *            the other object
	 * @return <code>true</code> if the two Objects have tested equals.
	 */
	public static boolean reflectionEquals(Object lhs, Object rhs) {
		return reflectionEquals(lhs, rhs, false, null, null);
	}

	/**
	 * <p>
	 * This method uses reflection to determine if the two <code>Object</code>s
	 * are equal.
	 * </p>
	 *
	 * <p>
	 * It uses <code>AccessibleObject.setAccessible</code> to gain access to
	 * private fields. This means that it will throw a security exception if run
	 * under a security manager, if the permissions are not set up correctly. It
	 * is also not as efficient as testing explicitly.
	 * </p>
	 *
	 * <p>
	 * Transient members will be not be tested, as they are likely derived
	 * fields, and not part of the value of the Object.
	 * </p>
	 *
	 * <p>
	 * Static fields will not be tested. Superclass fields will be included.
	 * </p>
	 *
	 * @param lhs
	 *            <code>this</code> object
	 * @param rhs
	 *            the other object
	 * @param excludeFields
	 *            array of field names to exclude from testing
	 * @return <code>true</code> if the two Objects have tested equals.
	 */
	public static boolean reflectionEquals(Object lhs, Object rhs, String[] excludeFields) {
		return reflectionEquals(lhs, rhs, false, null, excludeFields);
	}

	/**
	 * <p>
	 * This method uses reflection to determine if the two <code>Object</code>s
	 * are equal.
	 * </p>
	 *
	 * <p>
	 * It uses <code>AccessibleObject.setAccessible</code> to gain access to
	 * private fields. This means that it will throw a security exception if run
	 * under a security manager, if the permissions are not set up correctly. It
	 * is also not as efficient as testing explicitly.
	 * </p>
	 *
	 * <p>
	 * If the TestTransients parameter is set to <code>true</code>, transient
	 * members will be tested, otherwise they are ignored, as they are likely
	 * derived fields, and not part of the value of the <code>Object</code>.
	 * </p>
	 *
	 * <p>
	 * Static fields will not be tested. Superclass fields will be included.
	 * </p>
	 *
	 * @param lhs
	 *            <code>this</code> object
	 * @param rhs
	 *            the other object
	 * @param testTransients
	 *            whether to include transient fields
	 * @return <code>true</code> if the two Objects have tested equals.
	 */
	public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients) {
		return reflectionEquals(lhs, rhs, testTransients, null, null);
	}

	/**
	 * <p>
	 * This method uses reflection to determine if the two <code>Object</code>s
	 * are equal.
	 * </p>
	 *
	 * <p>
	 * It uses <code>AccessibleObject.setAccessible</code> to gain access to
	 * private fields. This means that it will throw a security exception if run
	 * under a security manager, if the permissions are not set up correctly. It
	 * is also not as efficient as testing explicitly.
	 * </p>
	 *
	 * <p>
	 * If the testTransients parameter is set to <code>true</code>, transient
	 * members will be tested, otherwise they are ignored, as they are likely
	 * derived fields, and not part of the value of the <code>Object</code>.
	 * </p>
	 *
	 * <p>
	 * Static fields will not be included. Superclass fields will be appended up
	 * to and including the specified superclass. A null superclass is treated
	 * as java.lang.Object.
	 * </p>
	 *
	 * @param lhs
	 *            <code>this</code> object
	 * @param rhs
	 *            the other object
	 * @param testTransients
	 *            whether to include transient fields
	 * @param reflectUpToClass
	 *            the superclass to reflect up to (inclusive), may be
	 *            <code>null</code>
	 * @return <code>true</code> if the two Objects have tested equals.
	 * @since 2.0
	 */
	public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients, Class reflectUpToClass) {
		return reflectionEquals(lhs, rhs, testTransients, reflectUpToClass, null);
	}

	/**
	 * <p>
	 * This method uses reflection to determine if the two <code>Object</code>s
	 * are equal.
	 * </p>
	 *
	 * <p>
	 * It uses <code>AccessibleObject.setAccessible</code> to gain access to
	 * private fields. This means that it will throw a security exception if run
	 * under a security manager, if the permissions are not set up correctly. It
	 * is also not as efficient as testing explicitly.
	 * </p>
	 *
	 * <p>
	 * If the testTransients parameter is set to <code>true</code>, transient
	 * members will be tested, otherwise they are ignored, as they are likely
	 * derived fields, and not part of the value of the <code>Object</code>.
	 * </p>
	 *
	 * <p>
	 * Static fields will not be included. Superclass fields will be appended up
	 * to and including the specified superclass. A null superclass is treated
	 * as java.lang.Object.
	 * </p>
	 *
	 * @param lhs
	 *            <code>this</code> object
	 * @param rhs
	 *            the other object
	 * @param testTransients
	 *            whether to include transient fields
	 * @param reflectUpToClass
	 *            the superclass to reflect up to (inclusive), may be
	 *            <code>null</code>
	 * @param excludeFields
	 *            array of field names to exclude from testing
	 * @return <code>true</code> if the two Objects have tested equals.
	 * @since 2.0
	 */
	public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients, Class reflectUpToClass,
			String[] excludeFields) {
		if (lhs == rhs) {
			return true;
		}
		if (lhs == null || rhs == null) {
			return false;
		}
		// Find the leaf class since there may be transients in the leaf
		// class or in classes between the leaf and root.
		// If we are not testing transients or a subclass has no ivars,
		// then a subclass can test equals to a superclass.
		Class lhsClass = lhs.getClass();
		Class rhsClass = rhs.getClass();
		Class testClass;
		if (lhsClass.isInstance(rhs)) {
			testClass = lhsClass;
			if (!rhsClass.isInstance(lhs)) {
				// rhsClass is a subclass of lhsClass
				testClass = rhsClass;
			}
		} else if (rhsClass.isInstance(lhs)) {
			testClass = rhsClass;
			if (!lhsClass.isInstance(rhs)) {
				// lhsClass is a subclass of rhsClass
				testClass = lhsClass;
			}
		} else {
			// The two classes are not related.
			return false;
		}

		//如果是字符串，直接比较
		if (testClass.equals(String.class)) {
			return lhs.equals(rhs);
		}

		AdvEqualsBuilder equalsBuilder = new AdvEqualsBuilder();
		try {
			reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields);
			while (testClass.getSuperclass() != null && testClass != reflectUpToClass) {
				testClass = testClass.getSuperclass();
				reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields);
			}
		} catch (IllegalArgumentException e) {
			// In this case, we tried to test a subclass vs. a superclass and
			// the subclass has ivars or the ivars are transient and
			// we are testing transients.
			// If a subclass has ivars that we are trying to test them, we get
			// an
			// exception and we know that the objects are not equal.
			return false;
		}
		return equalsBuilder.isEquals();
	}

	/**
	 * <p>
	 * Appends the fields and values defined by the given object of the given
	 * Class.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand object
	 * @param rhs
	 *            the right hand object
	 * @param clazz
	 *            the class to append details of
	 * @param builder
	 *            the builder to append to
	 * @param useTransients
	 *            whether to test transient fields
	 * @param excludeFields
	 *            array of field names to exclude from testing
	 */
	private static void reflectionAppend(Object lhs, Object rhs, Class clazz, AdvEqualsBuilder builder,
			boolean useTransients, String[] excludeFields) {
		Field[] fields = clazz.getDeclaredFields();
		List excludedFieldList = excludeFields != null ? Arrays.asList(excludeFields) : Collections.EMPTY_LIST;
		AccessibleObject.setAccessible(fields, true);
		for (int i = 0; i < fields.length && builder.isEquals; i++) {
			Field f = fields[i];
			if (!excludedFieldList.contains(f.getName()) && (f.getName().indexOf('$') == -1) && (useTransients
					|| !Modifier.isTransient(f.getModifiers())) && (!Modifier.isStatic(f.getModifiers()))) {
				try {
					builder.append(f.get(lhs), f.get(rhs));
				} catch (IllegalAccessException e) {
					// this can't happen. Would get a Security exception instead
					// throw a runtime exception in case the impossible happens.
					throw new InternalError("Unexpected IllegalAccessException");
				}
			}
		}
	}

	// -------------------------------------------------------------------------

	/**
	 * <p>
	 * Adds the result of <code>super.equals()</code> to this builder.
	 * </p>
	 *
	 * @param superEquals
	 *            the result of calling <code>super.equals()</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 * @since 2.0
	 */
	public AdvEqualsBuilder appendSuper(boolean superEquals) {
		if (isEquals == false) {
			return this;
		}
		isEquals = superEquals;
		return this;
	}

	// -------------------------------------------------------------------------

	/**
	 * <p>
	 * Test if two <code>Object</code>s are equal using their
	 * <code>equals</code> method.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand object
	 * @param rhs
	 *            the right hand object
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(Object lhs, Object rhs) {
		if (isEquals == false) {
			return this;
		}
		if (lhs == rhs) {
			return this;
		}
		if (lhs == null || rhs == null) {
			this.setEquals(false);
			return this;
		}
		Class lhsClass = lhs.getClass();
		if (!lhsClass.isArray()) {
			if (lhs instanceof java.math.BigDecimal) {
				isEquals = (((java.math.BigDecimal) lhs).compareTo((java.math.BigDecimal) rhs) == 0);
			} else if (lhs instanceof List) {
				isEquals = lhs.equals(rhs);

				for (int i = 0; i < ((List) lhs).size(); i++) {
					if (!isEquals()) {
						return this;
					}

					isEquals = reflectionEquals(((List) lhs).get(i), ((List) rhs).get(i));
				}
			} else if (lhs instanceof Map) {
				isEquals = lhs.equals(rhs);

				for (Object lkey : ((Map) lhs).keySet()) {
					if (!isEquals()) {
						return this;
					}
					isEquals = reflectionEquals(((Map) lhs).get(lkey), ((Map) rhs).get(lkey));
				}
			} else {
				// The simple case, not an array, just test the element
				isEquals = lhs.equals(rhs);
			}

		} else if (lhs.getClass() != rhs.getClass()) {
			// Here when we compare different dimensions, for example: a
			// boolean[][] to a boolean[]
			this.setEquals(false);
		}
		// 'Switch' on type of array, to dispatch to the correct handler
		// This handles multi dimensional arrays of the same depth
		else if (lhs instanceof long[]) {
			append((long[]) lhs, (long[]) rhs);
		} else if (lhs instanceof int[]) {
			append((int[]) lhs, (int[]) rhs);
		} else if (lhs instanceof short[]) {
			append((short[]) lhs, (short[]) rhs);
		} else if (lhs instanceof char[]) {
			append((char[]) lhs, (char[]) rhs);
		} else if (lhs instanceof byte[]) {
			append((byte[]) lhs, (byte[]) rhs);
		} else if (lhs instanceof double[]) {
			append((double[]) lhs, (double[]) rhs);
		} else if (lhs instanceof float[]) {
			append((float[]) lhs, (float[]) rhs);
		} else if (lhs instanceof boolean[]) {
			append((boolean[]) lhs, (boolean[]) rhs);
		} else {
			// Not an array of primitives
			append((Object[]) lhs, (Object[]) rhs);
		}
		return this;
	}

	/**
	 * <p>
	 * Test if two <code>long</code> s are equal.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>long</code>
	 * @param rhs
	 *            the right hand <code>long</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(long lhs, long rhs) {
		if (isEquals == false) {
			return this;
		}
		isEquals = (lhs == rhs);
		return this;
	}

	/**
	 * <p>
	 * Test if two <code>int</code>s are equal.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>int</code>
	 * @param rhs
	 *            the right hand <code>int</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(int lhs, int rhs) {
		if (isEquals == false) {
			return this;
		}
		isEquals = (lhs == rhs);
		return this;
	}

	/**
	 * <p>
	 * Test if two <code>short</code>s are equal.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>short</code>
	 * @param rhs
	 *            the right hand <code>short</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(short lhs, short rhs) {
		if (isEquals == false) {
			return this;
		}
		isEquals = (lhs == rhs);
		return this;
	}

	/**
	 * <p>
	 * Test if two <code>char</code>s are equal.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>char</code>
	 * @param rhs
	 *            the right hand <code>char</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(char lhs, char rhs) {
		if (isEquals == false) {
			return this;
		}
		isEquals = (lhs == rhs);
		return this;
	}

	/**
	 * <p>
	 * Test if two <code>byte</code>s are equal.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>byte</code>
	 * @param rhs
	 *            the right hand <code>byte</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(byte lhs, byte rhs) {
		if (isEquals == false) {
			return this;
		}
		isEquals = (lhs == rhs);
		return this;
	}

	/**
	 * <p>
	 * Test if two <code>double</code>s are equal by testing that the pattern of
	 * bits returned by <code>doubleToLong</code> are equal.
	 * </p>
	 *
	 * <p>
	 * This handles NaNs, Infinities, and <code>-0.0</code>.
	 * </p>
	 *
	 * <p>
	 * It is compatible with the hash code generated by
	 * <code>HashCodeBuilder</code>.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>double</code>
	 * @param rhs
	 *            the right hand <code>double</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(double lhs, double rhs) {
		if (isEquals == false) {
			return this;
		}
		return append(Double.doubleToLongBits(lhs), Double.doubleToLongBits(rhs));
	}

	/**
	 * <p>
	 * Test if two <code>float</code>s are equal byt testing that the pattern of
	 * bits returned by doubleToLong are equal.
	 * </p>
	 *
	 * <p>
	 * This handles NaNs, Infinities, and <code>-0.0</code>.
	 * </p>
	 *
	 * <p>
	 * It is compatible with the hash code generated by
	 * <code>HashCodeBuilder</code>.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>float</code>
	 * @param rhs
	 *            the right hand <code>float</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(float lhs, float rhs) {
		if (isEquals == false) {
			return this;
		}
		return append(Float.floatToIntBits(lhs), Float.floatToIntBits(rhs));
	}

	/**
	 * <p>
	 * Test if two <code>booleans</code>s are equal.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>boolean</code>
	 * @param rhs
	 *            the right hand <code>boolean</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(boolean lhs, boolean rhs) {
		if (isEquals == false) {
			return this;
		}
		isEquals = (lhs == rhs);
		return this;
	}

	/**
	 * <p>
	 * Performs a deep comparison of two <code>Object</code> arrays.
	 * </p>
	 *
	 * <p>
	 * This also will be called for the top level of multi-dimensional, ragged,
	 * and multi-typed arrays.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>Object[]</code>
	 * @param rhs
	 *            the right hand <code>Object[]</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(Object[] lhs, Object[] rhs) {
		if (isEquals == false) {
			return this;
		}
		if (lhs == rhs) {
			return this;
		}
		if (lhs == null || rhs == null) {
			this.setEquals(false);
			return this;
		}
		if (lhs.length != rhs.length) {
			this.setEquals(false);
			return this;
		}
		for (int i = 0; i < lhs.length && isEquals; ++i) {
			append(lhs[i], rhs[i]);
		}
		return this;
	}

	/**
	 * <p>
	 * Deep comparison of array of <code>long</code>. Length and all values are
	 * compared.
	 * </p>
	 *
	 * <p>
	 * The method {@link #append(long, long)} is used.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>long[]</code>
	 * @param rhs
	 *            the right hand <code>long[]</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(long[] lhs, long[] rhs) {
		if (isEquals == false) {
			return this;
		}
		if (lhs == rhs) {
			return this;
		}
		if (lhs == null || rhs == null) {
			this.setEquals(false);
			return this;
		}
		if (lhs.length != rhs.length) {
			this.setEquals(false);
			return this;
		}
		for (int i = 0; i < lhs.length && isEquals; ++i) {
			append(lhs[i], rhs[i]);
		}
		return this;
	}

	/**
	 * <p>
	 * Deep comparison of array of <code>int</code>. Length and all values are
	 * compared.
	 * </p>
	 *
	 * <p>
	 * The method {@link #append(int, int)} is used.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>int[]</code>
	 * @param rhs
	 *            the right hand <code>int[]</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(int[] lhs, int[] rhs) {
		if (isEquals == false) {
			return this;
		}
		if (lhs == rhs) {
			return this;
		}
		if (lhs == null || rhs == null) {
			this.setEquals(false);
			return this;
		}
		if (lhs.length != rhs.length) {
			this.setEquals(false);
			return this;
		}
		for (int i = 0; i < lhs.length && isEquals; ++i) {
			append(lhs[i], rhs[i]);
		}
		return this;
	}

	/**
	 * <p>
	 * Deep comparison of array of <code>short</code>. Length and all values are
	 * compared.
	 * </p>
	 *
	 * <p>
	 * The method {@link #append(short, short)} is used.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>short[]</code>
	 * @param rhs
	 *            the right hand <code>short[]</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(short[] lhs, short[] rhs) {
		if (isEquals == false) {
			return this;
		}
		if (lhs == rhs) {
			return this;
		}
		if (lhs == null || rhs == null) {
			this.setEquals(false);
			return this;
		}
		if (lhs.length != rhs.length) {
			this.setEquals(false);
			return this;
		}
		for (int i = 0; i < lhs.length && isEquals; ++i) {
			append(lhs[i], rhs[i]);
		}
		return this;
	}

	/**
	 * <p>
	 * Deep comparison of array of <code>char</code>. Length and all values are
	 * compared.
	 * </p>
	 *
	 * <p>
	 * The method {@link #append(char, char)} is used.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>char[]</code>
	 * @param rhs
	 *            the right hand <code>char[]</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(char[] lhs, char[] rhs) {
		if (isEquals == false) {
			return this;
		}
		if (lhs == rhs) {
			return this;
		}
		if (lhs == null || rhs == null) {
			this.setEquals(false);
			return this;
		}
		if (lhs.length != rhs.length) {
			this.setEquals(false);
			return this;
		}
		for (int i = 0; i < lhs.length && isEquals; ++i) {
			append(lhs[i], rhs[i]);
		}
		return this;
	}

	/**
	 * <p>
	 * Deep comparison of array of <code>byte</code>. Length and all values are
	 * compared.
	 * </p>
	 *
	 * <p>
	 * The method {@link #append(byte, byte)} is used.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>byte[]</code>
	 * @param rhs
	 *            the right hand <code>byte[]</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(byte[] lhs, byte[] rhs) {
		if (isEquals == false) {
			return this;
		}
		if (lhs == rhs) {
			return this;
		}
		if (lhs == null || rhs == null) {
			this.setEquals(false);
			return this;
		}
		if (lhs.length != rhs.length) {
			this.setEquals(false);
			return this;
		}
		for (int i = 0; i < lhs.length && isEquals; ++i) {
			append(lhs[i], rhs[i]);
		}
		return this;
	}

	/**
	 * <p>
	 * Deep comparison of array of <code>double</code>. Length and all values
	 * are compared.
	 * </p>
	 *
	 * <p>
	 * The method {@link #append(double, double)} is used.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>double[]</code>
	 * @param rhs
	 *            the right hand <code>double[]</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(double[] lhs, double[] rhs) {
		if (isEquals == false) {
			return this;
		}
		if (lhs == rhs) {
			return this;
		}
		if (lhs == null || rhs == null) {
			this.setEquals(false);
			return this;
		}
		if (lhs.length != rhs.length) {
			this.setEquals(false);
			return this;
		}
		for (int i = 0; i < lhs.length && isEquals; ++i) {
			append(lhs[i], rhs[i]);
		}
		return this;
	}

	/**
	 * <p>
	 * Deep comparison of array of <code>float</code>. Length and all values are
	 * compared.
	 * </p>
	 *
	 * <p>
	 * The method {@link #append(float, float)} is used.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>float[]</code>
	 * @param rhs
	 *            the right hand <code>float[]</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(float[] lhs, float[] rhs) {
		if (isEquals == false) {
			return this;
		}
		if (lhs == rhs) {
			return this;
		}
		if (lhs == null || rhs == null) {
			this.setEquals(false);
			return this;
		}
		if (lhs.length != rhs.length) {
			this.setEquals(false);
			return this;
		}
		for (int i = 0; i < lhs.length && isEquals; ++i) {
			append(lhs[i], rhs[i]);
		}
		return this;
	}

	/**
	 * <p>
	 * Deep comparison of array of <code>boolean</code>. Length and all values
	 * are compared.
	 * </p>
	 *
	 * <p>
	 * The method {@link #append(boolean, boolean)} is used.
	 * </p>
	 *
	 * @param lhs
	 *            the left hand <code>boolean[]</code>
	 * @param rhs
	 *            the right hand <code>boolean[]</code>
	 * @return AdvEqualsBuilder - used to chain calls.
	 */
	public AdvEqualsBuilder append(boolean[] lhs, boolean[] rhs) {
		if (isEquals == false) {
			return this;
		}
		if (lhs == rhs) {
			return this;
		}
		if (lhs == null || rhs == null) {
			this.setEquals(false);
			return this;
		}
		if (lhs.length != rhs.length) {
			this.setEquals(false);
			return this;
		}
		for (int i = 0; i < lhs.length && isEquals; ++i) {
			append(lhs[i], rhs[i]);
		}
		return this;
	}

	/**
	 * <p>
	 * Returns <code>true</code> if the fields that have been checked are all
	 * equal.
	 * </p>
	 *
	 * @return boolean
	 */
	public boolean isEquals() {
		return this.isEquals;
	}

	/**
	 * Sets the <code>isEquals</code> value.
	 *
	 * @param isEquals
	 *            The value to set.
	 * @since 2.1
	 */
	protected void setEquals(boolean isEquals) {
		this.isEquals = isEquals;
	}
}
